Pressidian
花园入口
笔记
项目
关于
实验室
GitHub
花园入口
笔记
项目
关于
实验室
GitHub

KNOWLEDGE PATHS

笔记库
当前位置
笔记库/前端/面试/代码/正则表达式

解析模板字符串

2 分钟阅读 · Note

目录树 578 篇

            • 解析模板字符串
            • Agent中的正则
          • 面试-常考经典1
          • 面试-常考经典2
          • 面试-数组专项
        • 可投递企业
      • 前端技术栈
    • 笔记目录
    • CLAUDE.md
    • Vue 组件与 Render 函数

关联笔记 6

↗Agent中的正则同一路径↗5 最长回文子串 ⌚️共同主题↗面试-常考经典1共同主题↗面试-常考经典2共同主题↗面试-数组专项共同主题↗牛客 101共同主题
  • 解析模板字符串

解析模板字符串

// tpl(
//     'Hello, ${user.name} !' , 
//     { user: { name: 'Hanmeimei' } }
// ) 

// 打印 Hello, Hanmeimei !

function tpl(template, data) {
  return template.replace(/\$\{([^}]+)\}/g, (match, expr) => {
    // 去除表达式首尾空格,并沿点号路径读取值
    const value = expr.trim().split('.').reduce((obj, key) => obj?.[key], data);
    // 如果取到的值是 undefined 或 null,返回空字符串,否则转为字符串
    return value ?? '';
  });
}

没问题,正则表达式一开始看确实像“天书”,但如果把它拆开来看,其实逻辑非常清晰。

这个正则表达式 /\$\{([^}]+)\}/g 的核心作用是:在一长串文本中,精准找到所有被 ${ 和 } 包裹的内容,并把中间的内容提取出来。

1. / 和 /g:正则的“外壳”和“指令”

  • / ... /:这是 JavaScript 中正则表达式的固定写法,就像字符串用 '' 包裹一样,正则表达式用 / 包裹。
  • g:这是全局匹配标志(Global)。意思是“不要只找第一个,要把字符串里所有符合条件的都找出来”。

2. \$:匹配真实的 $ 符号

  • 在正则表达式中,$ 是一个有特殊含义的符号(代表字符串结尾)。
  • 如果我们只想匹配普通的美元符号 $,就必须在前面加一个反斜杠 \ 进行转义。所以 \$ 就代表匹配真实的 $ 字符。

3. \{:匹配真实的 { 符号

  • 同理,{ 在正则里也有特殊含义(用来表示数量,比如 {3} 表示重复3次)。
  • 为了匹配真实的左花括号 {,也需要转义写成 \{。
  • 小结:\$\{ 连在一起,就是告诉程序:“请在这里寻找真实的 ${ 这两个字符”。

4. ([^}]+):核心捕获组(提取内容)

这是整个正则最核心的部分,用来提取 ${} 里面的变量名。

  • ( 和 ):这叫捕获组。它的作用就像一个“套索”,把里面匹配到的内容单独拎出来。在 JS 的 replace 方法中,括号里的内容会作为回调函数的参数传给我们。
  • [^}]:方括号 [] 表示字符集合,前面加上 ^ 表示**“取反”**。所以 [^}] 的意思是:匹配任何不是 } 的字符。这就保证了匹配过程不会跨越多个 ${}。
  • +:表示“一次或多次”。
  • 小结:([^}]+) 连在一起,就是告诉程序:“请尽可能多地匹配字符,直到遇到 } 为止,并且把这些字符单独提取出来”。

5. \}:匹配真实的 } 符号

  • 和前面的 \{ 一样,用来匹配真实的右花括号 },作为占位符的结束标志。

解析模板字符串我的实现,共两个:

// tpl(
//     'Hello, ${user.name} !' , 
//     { user: { name: 'Hanmeimei' } }
// ) 

function tpl(str, obj) {
    if (str.length === 0) return ''
    if (obj === null && typeof obj !== "object") return new Error('obj is not a object!!!')
    const n = str.length

    let start = 0
    let end = n - 1

    while (start < end) {
        console.log(start, end)
        if (start !== '{') start++
        if (end !== '}') end--
    }

    console.log(start, end)

    let path = str.slice(start, end + 1).split('.')

    console.log(path, 'path')

    let value = obj
    for(let i = 0 ;i< path.length;i++){
        value = value[path[i]]
    }

    let ans =  str.slice(0, start - 1) + value + str.slice(end + 1, n)

    return ans
}

tpl('Hello, ${user.name} !', { user: { name: 'Hanmeimei' } })

// =======第二个实现========
// tpl(
//     'Hello, ${user.name} !' ,
//     { user: { name: 'Hanmeimei' } }
// )
function tpl(str, obj) {
    if (str.length === 0) return ''
    if (obj === null && typeof obj !== 'object')
        return new Error('obj is not a object!!!')
    const n = str.length

    let start = str.indexOf('{')
    let end = str.indexOf('}')

    console.log(start, end)

    let path = str.slice(start + 1, end).split('.')

    console.log(path, 'path')

    let value = obj
    for (let i = 0; i < path.length; i++) {
        value = value[path[i]]
    }

    console.log(value);
    

    let ans = str.slice(0, start - 1) + value + str.slice(end + 1, n)

    return ans
}

console.log(tpl('Hello, ${user.name} !', { user: { name: 'Hanmeimei' } }))
// Hello, Hanmeimei !